package gridfs.dao;

import com.mongodb.ReadConcern;
import com.mongodb.WriteConcern;
import com.mongodb.client.MongoDatabase;
import jnr.ffi.Pointer;
import org.bson.Document;
import org.bson.types.ObjectId;
import ru.serce.jnrfuse.FuseFillDir;

import java.io.Closeable;
import java.io.IOException;
import java.util.List;

/**
 * @author siuming
 */
public class GridFsAdaptor implements Closeable {

    private MongoDatabase database;
    private ReadConcern readConcern;
    private WriteConcern writeConcern;

    private String bucketName;
    private int chunkSize;

//    private ExecutorService executorService;

    GridFsAdaptor(Builder builder) {
        this.database = builder.database;
        this.readConcern = builder.readConcern;
        this.writeConcern = builder.writeConcern;
        this.bucketName = builder.bucketName;
        this.chunkSize = builder.chunkSize;
//        this.executorService = Executors.newFixedThreadPool(builder.maxThreads);
    }

    public String getBucketName(){
        return bucketName;
    }
    public void setBucketName(String bucketName){
        this.bucketName = bucketName;
    }

    public GridFile get(String filename, String bucketName0) {
        GridFileBucket bucket = new GridFileBucket(database, readConcern, writeConcern, bucketName0, chunkSize);
        return bucket.get(filename);
    }

    public ObjectId create(String filename, String dirname, String bucketName0) {
        GridFileBucket bucket = new GridFileBucket(database, readConcern, writeConcern, bucketName0, chunkSize);
        return bucket.create(filename, dirname,
                new Document()
                        .append("isFile", true));
    }

    public ObjectId mkdir(String filename, String dirname, String bucketName0) {
        GridFileBucket bucket = new GridFileBucket(database, readConcern, writeConcern, bucketName0, chunkSize);
        return bucket.create(filename, dirname,
                new Document()
                        .append("isDir", true));
    }

    public void delete(String filename, String bucketName0) {
        GridFileBucket bucket = new GridFileBucket(database, readConcern, writeConcern, bucketName0, chunkSize);
        bucket.delete(bucket.get(filename).getId());
    }

    public int read(Pointer buf, String filename, long size, long offset, String bucketName0) {
        GridFileBucket bucket = new GridFileBucket(database, readConcern, writeConcern, bucketName0, chunkSize);
        GridFile file = bucket.get(filename);
        byte[] bytesRead = bucket.read(file.getId(), size, offset);
        buf.put(0, bytesRead, 0, bytesRead.length);
        return bytesRead.length;
    }

    public int write(Pointer buf, String filename, long offset, long size, String bucketName0) {
        GridFileBucket bucket = new GridFileBucket(database, readConcern, writeConcern, bucketName0, chunkSize);
        GridFile file = bucket.get(filename);
        bucket.write(file.getId(), buf, offset, size);
        return (int)size;
    }

    public List<String> readdir(String dirname, Pointer buf, FuseFillDir filter, String bucketName0) {
        GridFileBucket bucket = new GridFileBucket(database, readConcern, writeConcern, bucketName0, chunkSize);
        return bucket.readdir(dirname);
    }

    public void truncate(ObjectId id, long length, String bucketName0) {
        GridFileBucket bucket = new GridFileBucket(database, readConcern, writeConcern, bucketName0, chunkSize);
        bucket.truncate(id, length);
    }

    public void rename(String path, String newName, String bucketName0) {
        GridFileBucket bucket = new GridFileBucket(database, readConcern, writeConcern, bucketName0, chunkSize);
        bucket.rename(bucket.get(path).getId(),newName);
    }

//    public CompletableFuture<Void> delete(String filename, String bucketName0) {
//        GridFileBucket bucket = new GridFileBucket(database, readConcern, writeConcern, Optional.of(bucketName0).orElse(bucketName), chunkSize);
//        ObjectId id = bucket.get(filename).getId();
//        return CompletableFuture
//                .supplyAsync(() -> bucket.get(filename).getId(), executorService)
//                .thenApplyAsync(id -> {
//                    bucket.delete(id);
//                    return null;
//                }, executorService);
//    }

    @Override
    public void close() throws IOException {
//        executorService.shutdown();
    }

    /**
     * @return
     */
    public static Builder newBuilder() {
        return new Builder();
    }

    public static class Builder {

        MongoDatabase database;
        ReadConcern readConcern = ReadConcern.DEFAULT;
        WriteConcern writeConcern = WriteConcern.ACKNOWLEDGED;

        String bucketName = "public";
        int chunkSize = 255 * 1024;
        int maxThreads = 50;

        Builder() {
        }

        public Builder database(MongoDatabase database) {
            this.database = database;
            return this;
        }

        public Builder readConcern(ReadConcern readConcern) {
            this.readConcern = readConcern;
            return this;
        }

        public Builder writeConcern(WriteConcern writeConcern) {
            this.writeConcern = writeConcern;
            return this;
        }

        public Builder bucketName(String bucketName) {
            this.bucketName = bucketName;
            return this;
        }

        public Builder chunkSize(int chunkSize) {
            this.chunkSize = chunkSize;
            return this;
        }

        public Builder maxThreads(int maxThreads) {
            this.maxThreads = maxThreads;
            return this;
        }

        public GridFsAdaptor build() {
            return new GridFsAdaptor(this);
        }
    }
}
